#include <windows.h>
#include <extend.h>
#include <dbf.h>
#include <cdx.h>

void DbGoTop( void );
void DbGoTo( long lGoRec );
void UpdateRecord( void );

#define F_BEGIN  0
#define F_CURPOS 1
#define F_END    2

typedef struct
{
   HANDLE   hFile;
   DBFHEAD  header;
   WORD     wFields;
   PFIELD   pFields;
   LPVOID   pRecord;
   LONG     lRecNo;
   PCDXINFO cdxinfo;
} WORKAREA, * PWORKAREA;

WORKAREA workareas[ 255 ];
WORD     selArea = 0;

//----------------------------------------------------------------------------//

void _dbUseArea( BOOL lNewArea, LPSTR szDriver, LPSTR szFileName, LPSTR szAlias,
                 BOOL lShared, BOOL lReadonly )
{
   WORD w, wFields = 0, wRecSize, wOffset = 1; // 0 wOffset is deleted
   HANDLE hFile;
   FIELD f;
   PFIELD pFields;
   char szFileIndexName[250];
   int i;
   HANDLE hFileCdx;

   if( ( hFile = _fopen( szFileName,
                         FO_READWRITE | IF( lShared, FO_SHARED, 0 ) ) ) == -1 )
   {
      MsgAlert( "Could not open DBF!", "Error" );
      return;
   }

   workareas[ selArea ].hFile = hFile;
   _fread( hFile, ( LPBYTE ) &workareas[ selArea ].header, sizeof( DBFHEAD ) );
   _fread( hFile, ( LPBYTE ) &f, sizeof( FIELD ) );

   i = 1;

   while( i <= ( workareas[ selArea ].header.wDataOffset - 32 ) / sizeof( FIELD ) )
   {
      wFields++;
      _fread( hFile, ( LPBYTE ) &f, sizeof( FIELD ) );
      i++;
   }

   workareas[ selArea ].wFields = wFields;
   _fseek( hFile, sizeof( DBFHEAD ), 0 );
   pFields = _vmLock( _vmAlloc( ( wFields * sizeof( FIELD ) ) ) );
   _fread( hFile, ( LPVOID ) pFields, ( wFields * sizeof( FIELD ) ) );

   for( w = 0; w < wFields; w++ )
   {
      pFields[ w ].wOffset = wOffset;
      wOffset += pFields[ w ].bCharLen;
   }

   workareas[ selArea ].pFields = pFields;
   wRecSize = workareas[ selArea ].header.wRecSize;
   workareas[ selArea ].pRecord = _vmLock( _vmAlloc( wRecSize ) );

   workareas[ selArea ].cdxinfo = NULL;
   if ( workareas[ selArea ].header.bProdIndex  )
   {
      // There is a Cdx file index, open it !!
      for ( i = 0; i < ( _strat(".",szFileName) -1 ); i++ )
      {
         szFileIndexName[ i ] = *( szFileName + i );
      }
      szFileIndexName[ i ] = 0 ;
      _strcat(szFileIndexName,".CDX");

      // Allocate memory for cdxinfo

      workareas[ selArea ].cdxinfo = _vmLock( _vmAlloc( sizeof(CDXINFO) ) );

      hFileCdx = _fopen(szFileIndexName , FO_READWRITE | FO_SHARED );
      if ( hFileCdx != -1 )
      {
        if ( ! cdxopen( hFileCdx , workareas[ selArea ].cdxinfo , NULL ) )
        {
            MsgAlert( "Could not open CDX index file!", "Error" );
            _vmFree( workareas[ selArea ].cdxinfo );
            workareas[ selArea ].cdxinfo   = NULL ;
            _fclose(hFileCdx);
        }
      }
      else
      {
            MsgAlert( "Could not create CDX index file!", "Error" );
            _vmFree( workareas[ selArea ].cdxinfo );
            workareas[ selArea ].cdxinfo   = NULL ;
      }
   }
   DbGoTop();
}

//----------------------------------------------------------------------------//

void DbGoTop( void )
{
   long lCdxRec;

   if  (workareas[ selArea ].cdxinfo)
        if  ( ! workareas[ selArea ].cdxinfo->TagName[ 0 ] )
        {
            // No Tag active
                _fseek( workareas[ selArea ].hFile,
                        sizeof( DBFHEAD ) +
                        ( workareas[ selArea ].wFields * sizeof( FIELD ) ) + 2,
                        0 );
                _fread( workareas[ selArea ].hFile, workareas[ selArea ].pRecord,
                        workareas[ selArea ].header.wRecSize );
                _fseek( workareas[ selArea ].hFile,
                        sizeof( DBFHEAD ) +
                        ( workareas[ selArea ].wFields * sizeof( FIELD ) ) + 2,
                        0 );
                workareas[ selArea ].lRecNo = 1;
        }
        else
        {
            // Go top using Index
            lCdxRec = cdxgotop(workareas[ selArea ].cdxinfo) ;
            if ( lCdxRec != -1 )
                DbGoTo( lCdxRec );
            else
            {
                // Error not record for Go top
            }
        }
}

//----------------------------------------------------------------------------//

void DbCloseArea( void )
{
   _fclose( workareas[ selArea ].hFile );
   workareas[ selArea ].hFile = 0;

   if ( workareas[ selArea ].cdxinfo )
   {
      _fclose(workareas[ selArea ].cdxinfo->cdxfile);
      _vmFree( workareas[ selArea ].cdxinfo );
   }
   _vmFree( workareas[ selArea ].pFields );
   _vmFree( workareas[ selArea ].pRecord );
}

//----------------------------------------------------------------------------//

void DbGoBottom( void )
{
   long lCdxRec;

   if  (workareas[ selArea ].cdxinfo)
        if  ( ! workareas[ selArea ].cdxinfo->TagName[ 0 ] )
        {
                workareas[ selArea ].lRecNo = _fseek( workareas[ selArea ].hFile,
                    0 - workareas[ selArea ].header.wRecSize - 1, 2 ) /
                    workareas[ selArea ].header.wRecSize;

                _fread( workareas[ selArea ].hFile, workareas[ selArea ].pRecord,
                        workareas[ selArea ].header.wRecSize );

                _fseek( workareas[ selArea ].hFile,
                        0 - workareas[ selArea ].header.wRecSize - 1, 2 );
        }
        else
        {
            // Go Bottom using Index
            lCdxRec = cdxgobottom(workareas[ selArea ].cdxinfo) ;
            if ( lCdxRec != -1 )
                DbGoTo( lCdxRec );
            else
            {
                // Error not record for Go bottom
            }
        }
}

//----------------------------------------------------------------------------//

void DbGoTo( long lGoRec )
{
   long lnewpos = ( ( lGoRec -1)
                  * ( workareas[ selArea ].header.wRecSize  ) +
                  workareas[ selArea ].header.wDataOffset  );

    _fseek( workareas[ selArea ].hFile, lnewpos , 0 );

    _fread( workareas[ selArea ].hFile, workareas[ selArea ].pRecord,
            workareas[ selArea ].header.wRecSize );

    _fseek( workareas[ selArea ].hFile, lnewpos , 0 );

    workareas[ selArea ].lRecNo = lGoRec;
}

//----------------------------------------------------------------------------//

LONG _recsize( void )
{
   if( workareas[ selArea ].hFile )
      return workareas[ selArea ].header.wRecSize;
   else
      return 0;
}

//----------------------------------------------------------------------------//

LPSTR FieldGetC( WORD wField )
{
   UpdateRecord();
   return ( LPSTR ) workareas[ selArea ].pRecord +
          workareas[ selArea ].pFields[ wField - 1 ].wOffset;
}

//----------------------------------------------------------------------------//

static WORD _min( WORD w1, WORD w2 )
{
   if( w1 < w2 )
      return w1;
   else
      return w2;
}

//----------------------------------------------------------------------------//

void FieldPutC( WORD wField, LPSTR szText )
{
   WORD wLen = min( _strlen( szText ),
                    workareas[ selArea ].pFields[ wField - 1 ].bCharLen );

   _bcopy( ( LPSTR ) workareas[ selArea ].pRecord +
           workareas[ selArea ].pFields[ wField - 1 ].wOffset,
           szText, wLen );

   _bset( ( LPSTR ) workareas[ selArea ].pRecord +
          workareas[ selArea ].pFields[ wField - 1 ].wOffset + wLen,
          0, workareas[ selArea ].pFields[ wField - 1 ].bCharLen - wLen );
}

//----------------------------------------------------------------------------//

BOOL FieldGetL( WORD wField )
{
   UpdateRecord();
   return ( BOOL ) * ( ( LPBYTE ) workareas[ selArea ].pRecord +
          workareas[ selArea ].pFields[ wField - 1 ].wOffset ) == 'T';
}

//----------------------------------------------------------------------------//

void FieldPutL( WORD wField, BOOL bValue )
{
   * ( ( LPBYTE ) workareas[ selArea ].pRecord +
     workareas[ selArea ].pFields[ wField - 1 ].wOffset ) =
     IF( bValue, 'T', 'F' );
}

//----------------------------------------------------------------------------//

WORD FieldLen( WORD wField )
{
   return workareas[ selArea ].pFields[ wField - 1 ].bCharLen;
}

//----------------------------------------------------------------------------//

LPSTR FieldName( WORD wField )
{
   return workareas[ selArea ].pFields[ wField - 1 ].szName;
}

//----------------------------------------------------------------------------//

BYTE FieldType( WORD wField )
{
   return workareas[ selArea ].pFields[ wField - 1 ].bType;
}

//----------------------------------------------------------------------------//

WORD FCount( void )
{
   return workareas[ selArea ].wFields;
}

//----------------------------------------------------------------------------//

WORD FieldPos( LPSTR szField )
{
   WORD w = 0;

   _strupper( szField, szField );

   while( w < workareas[ selArea ].wFields )
   {
      if( _strcmp( workareas[ selArea ].pFields[ w ].szName, szField ) )
         return w + 1;
      w++;
   }
   return 0;
}

//----------------------------------------------------------------------------//

void DbSkip( long lRecs )
{
   long lCdxRec;

    if ( ( lRecs ) && ( workareas[ selArea ].cdxinfo ) )
        if (  ! workareas[ selArea ].cdxinfo->TagName[ 0 ] )
        {
            _fseek( workareas[ selArea ].hFile,
                    workareas[ selArea ].header.wRecSize * lRecs, 1 );

            _fread( workareas[ selArea ].hFile, workareas[ selArea ].pRecord,
                    workareas[ selArea ].header.wRecSize );

            _fseek( workareas[ selArea ].hFile,
                    -workareas[ selArea ].header.wRecSize, 1 );

            workareas[ selArea ].lRecNo += lRecs;
        }
        else
        {
            // Skip using Index
            lCdxRec = cdxskip(workareas[ selArea ].cdxinfo,lRecs) ;
            if ( lCdxRec != -1 )
                DbGoTo( lCdxRec );
            else
            {
                // Error not record for skip
            }
        }
}

//----------------------------------------------------------------------------//

LONG RecCount( void )
{
   return workareas[ selArea ].header.lnRecs;
}

//----------------------------------------------------------------------------//

LONG _recno( void )
{
   return workareas[ selArea ].lRecNo;
}

//----------------------------------------------------------------------------//

void saverec( void )
{
   /* ---------------------------------------------------------------------
    *   Update cdxindex with new data
    *
    ---------------------------------------------------------------------*/

   _fwrite( workareas[ selArea ].hFile, workareas[ selArea ].pRecord,
            workareas[ selArea ].header.wRecSize );

   _fseek( workareas[ selArea ].hFile, -workareas[ selArea ].header.wRecSize, 1 );
}

//----------------------------------------------------------------------------//

BOOL DbRLock( void )
{
   DWORD dwOffset = sizeof( DBFHEAD ) +
                    ( workareas[ selArea ].header.wRecSize *
                    ( workareas[ selArea ].lRecNo - 1 ) ); //  + 43000000000000;

   return LockFile( workareas[ selArea ].hFile,
                    LOWORD( dwOffset ), HIWORD( dwOffset ),
                    workareas[ selArea ].header.wRecSize, 0 );
}

//----------------------------------------------------------------------------//

BOOL DbRUnLock( void )
{
   DWORD dwOffset = sizeof( DBFHEAD ) +
                    ( workareas[ selArea ].header.wRecSize *
                    ( workareas[ selArea ].lRecNo - 1 ) ); //  + 43000000000000;

   return UnlockFile( workareas[ selArea ].hFile,
                      LOWORD( dwOffset ), HIWORD( dwOffset ),
                      workareas[ selArea ].header.wRecSize, 0 );
}

//----------------------------------------------------------------------------//
void DbTagByNam( LPSTR szTagName )     // e
{
   cdxsettagbyname( workareas[ selArea ].cdxinfo , szTagName);
}

//----------------------------------------------------------------------------//
void DbTagByPos(WORD wTagPos )
{
   cdxsettagbypos(workareas[ selArea ].cdxinfo , wTagPos );
}

//----------------------------------------------------------------------------//

void DbSeek( LPSTR szKey )
{
   cdxseek( workareas[ selArea ].cdxinfo ,
            (char *) szKey,
            TRUE,                       // SoftSeek
            CDXKEYCHAR );               // Key Type
}

//----------------------------------------------------------------------------//

CLIPPER DBRLOCK()
{
   _retl( DbRLock() );
}

//----------------------------------------------------------------------------//

CLIPPER DBSETORDER() //E
{
   if ( _ischar( 1 ) )
     DbTagByNam((char *) _parc( 1 ));
   else if( _isnum( 1 ) )
     DbTagByPos( _parni( 1 ) );

}

//----------------------------------------------------------------------------//

CLIPPER DBRUNLOCK()
{
   _retl( DbRUnLock() );
}

//----------------------------------------------------------------------------//

CLIPPER FIELDGET()
{
   WORD wField = _parni( 1 );

   switch( FieldType( wField ) )
   {
      case 'C':
           _retclen( FieldGetC( wField ), FieldLen( wField ) );
           break;

      case 'L':
           _retl( FieldGetL( wField ) );
           break;
   }
}

//----------------------------------------------------------------------------//

CLIPPER FIELDPUT()
{
   WORD wField = _parni( 1 );

   switch( FieldType( wField ) )
   {
      case 'C':
           FieldPutC( wField, _parc( 2 ) );
           break;

      case 'L':
           FieldPutL( wField, _parl( 2 ) );
           break;
   }
   saverec();
}

//----------------------------------------------------------------------------//

BOOL DbDelete( void )
{
   ( * ( LPBYTE ) workareas[ selArea ].pRecord ) = '*';
   saverec();
}

//----------------------------------------------------------------------------//

BOOL DbReCall( void )
{
   ( * ( LPBYTE ) workareas[ selArea ].pRecord ) = ' ';
   saverec();
}

//----------------------------------------------------------------------------//

BOOL Deleted( void )
{
   return ( BOOL ) ( ( * ( LPBYTE ) workareas[ selArea ].pRecord ) == '*' );
}

//----------------------------------------------------------------------------//

void DbAppend( void )
{
   _fseek( workareas[ selArea ].hFile, 0, F_BEGIN );
   workareas[ selArea ].header.lnRecs++;
   _fwrite( workareas[ selArea ].hFile, &workareas[ selArea ].header,
            sizeof( DBFHEAD ) );

   _fseek( workareas[ selArea ].hFile, 0, F_END );
   _bset( workareas[ selArea ].pRecord, 0,
          workareas[ selArea ].header.wRecSize + 1 );
   saverec();
}

//----------------------------------------------------------------------------//
void UpdateRecord( void )
{
   long lnewpos = ( ( workareas[ selArea ].lRecNo -1)
                  * ( workareas[ selArea ].header.wRecSize  ) +
                  workareas[ selArea ].header.wDataOffset  );

    _fseek( workareas[ selArea ].hFile, lnewpos , 0 );

    _fread( workareas[ selArea ].hFile, workareas[ selArea ].pRecord,
            workareas[ selArea ].header.wRecSize );

    _fseek( workareas[ selArea ].hFile, lnewpos , 0 );
}


//----------------------------------------------------------------------------//

CLIPPER FIELDPOS()
{
   _retni( FieldPos( _parc( 1 ) ) );
}

//----------------------------------------------------------------------------//

CLIPPER FIELDTYPE()
{
   _retni( FieldType( _parni( 1 ) ) );
}

//----------------------------------------------------------------------------//

CLIPPER FCOUNT()
{
   _retni( FCount() );
}

//----------------------------------------------------------------------------//

CLIPPER FIELDNAME()
{
   _retc( FieldName( _parni( 1 ) ) );
}

//----------------------------------------------------------------------------//

CLIPPER RECSIZE()
{
   _retni( _recsize() );
}

//----------------------------------------------------------------------------//

CLIPPER RECNO()
{
   _retnl( _recno() );
}

//----------------------------------------------------------------------------//

CLIPPER DBUSEAREA()
{
   _dbUseArea( _parl( 1 ), _parc( 2 ), _parc( 3 ), _parc( 4 ), _parl( 5 ),
               _parl( 6 ) );
}

//----------------------------------------------------------------------------//

CLIPPER DBCLOSEARE() // A()
{
   DbCloseArea();
}

//----------------------------------------------------------------------------//

CLIPPER DBGOTOP()
{
   DbGoTop();
}

//----------------------------------------------------------------------------//

CLIPPER DBGOBOTTOM()
{
   DbGoBottom();
}

//----------------------------------------------------------------------------//

CLIPPER DBGOTO()
{
   DbGoTo( _parni( 1 ));
}

//----------------------------------------------------------------------------//

CLIPPER DBSKIP()
{
   DbSkip( IF( _pcount() > 0, _parnl( 1 ), 1 ) );
}

//----------------------------------------------------------------------------//

CLIPPER DELETED()
{
   _retl( Deleted() );
}

//----------------------------------------------------------------------------//

CLIPPER DBDELETE()
{
   DbDelete();
}

//----------------------------------------------------------------------------//

CLIPPER DBRECALL()
{
   DbReCall();
}

//----------------------------------------------------------------------------//

CLIPPER DBSEEK()
{
   DbSeek( (char *)_parc( 1 ) );
}


//----------------------------------------------------------------------------//

CLIPPER RECCOUNT()
{
   _retnl( RecCount() );
}

//----------------------------------------------------------------------------//

CLIPPER DBAPPEND()
{
   DbAppend();
}

//----------------------------------------------------------------------------//

CLIPPER DBSELECTAR() // EA()
{
   if( _isnum( 1 ) )
      selArea = _parni( 1 );

   if( _ischar( 1 ) ); // pending
}

//----------------------------------------------------------------------------//